use async_graphql::{Context, Enum, ID};
use derivative::Derivative;
use rbatis::rbatis::Rbatis;
use serde::{Deserialize, Serialize};
use serde_json::Result;

use crate::features::gfilter::{
  self, DateTimeFilterIn, IntFilterIn, StringExactFilterIn, StringPatternFilterIn,
};
use crate::features::quizzes::modelsgqo::{self, QuizItem};
use crate::features::quizzes::repository::{self};
use crate::features::quizzes::{self};
use crate::features::tasks::modelsdbo::TaskDBO;
use crate::features::tasks::services::{self};

#[derive(async_graphql::SimpleObject, Serialize, Deserialize, Clone, Debug)]
#[graphql(complex)]
pub struct Task {
  pub id: ID,
  pub snapshot_at: String,
  pub subject: String,
  pub title: Option<String>,
  pub description: Option<String>,

  pub assignee_id: String,

  pub quiz_item_id: String,

  pub ebp_last_at: Option<i64>,
  pub ebp_times_total: Option<u32>,

  pub ebp_difficulty: usize,
  pub ebp_forgetproba: usize,
  pub ebp_recalltime: i64,

  /// local created timestamp, seconds since 1970-01-01
  pub created_at: i64,
  /// local updated timestamp, seconds since 1970-01-01
  pub updated_at: i64,

  pub status: Option<String>,
}

#[async_graphql::ComplexObject]
impl Task {
  pub async fn from(&self) -> String {
    let mut from = String::new();
    from.push_str(&self.id);
    from.push_str("<");
    from.push_str(&self.quiz_item_id.clone());
    from.push_str(">");

    from
  }

  /// get resource node by quiz_item_id
  pub async fn quiz_item(&self, ctx: &Context<'_>) -> Option<QuizItem> {
    let rbatis_pool = ctx.data_unchecked::<Rbatis>();
    let qitem_dbo = quizzes::repository::get_quiz_item_by_id(rbatis_pool, &self.quiz_item_id).await;

    if qitem_dbo.is_ok() {
      let qitem_dbo_ = qitem_dbo.unwrap();
      let qitem: QuizItem = qitem_dbo_.into();
      Some(qitem)
    } else {
      None
    }
  }
}

impl From<TaskDBO> for Task {
  fn from(item: TaskDBO) -> Self {
    Task {
      id: ID::from(item.id),
      snapshot_at: item.snapshot_at,
      subject: item.subject,
      title: item.title,
      description: item.description,

      assignee_id: item.assignee_id,
      quiz_item_id: item.quiz_item_id,

      ebp_last_at: item.ebp_last_at,
      ebp_times_total: item.ebp_times_total,

      ebp_difficulty: item.ebp_difficulty,
      ebp_forgetproba: item.ebp_forgetproba,
      ebp_recalltime: item.ebp_recalltime,

      created_at: item.created_at,
      updated_at: item.updated_at,
      status: item.status,
    }
  }
}

#[derive(async_graphql::InputObject, Serialize, Deserialize, Clone, Debug)]
pub struct NewTask {
  pub id: String,
  pub ver: u32,
  pub subject: String,
  pub title: String,
  pub description: String,
  pub content_ids: String,
  #[graphql(skip)]
  pub created_at: i64,
  #[graphql(skip)]
  pub updated_at: i64,
  pub status: String,
}

#[derive(async_graphql::InputObject, Serialize, Deserialize, Clone, Debug)]
pub struct AnswerTaskQuizItemIn {
  /// task id for this answer
  pub task_id: ID,
  /// official answer
  pub std_answer: String,
  /// your answer
  pub answer: String,
  /// answered in seconds
  pub seconds: Option<u32>,
}

#[derive(async_graphql::InputObject, Serialize, Deserialize, Clone, Debug)]
pub struct TaskFilterIn {
  /// default condition fields logic relation, AND the provided conditions
  pub and: Option<Vec<TaskFilterIn>>,

  /// or condition fields logic relation, OR the provided conditions
  pub or: Option<Vec<TaskFilterIn>>,

  /// not condition fields logic relation, not the provided condition
  pub not: Box<Option<TaskFilterIn>>,

  pub subject: Option<StringExactFilterIn>,

  /// 回忆难度，e.g. 9,8,7,6,5,4,3,2,1,0
  pub ebp_difficulty: Option<IntFilterIn>,
  /// 回忆概率,e.g. 100,70,50 分别表示概率100%，70%，50%
  pub ebp_forgetproba: Option<IntFilterIn>,
  /// 回忆时间，回忆概率对应的开始时间,自 1970/01/01 00:00:00 UTC 起經過的秒數
  pub ebp_recalltime: Option<IntFilterIn>,
}

#[derive(async_graphql::SimpleObject, Serialize, Deserialize, Clone, Debug, Derivative)]
#[derivative(Default)]
#[graphql(complex)]
pub struct RecallProbability {
  pub recallpb: i16,
  pub title: String,
  pub tooltip: String,

  pub task_count: usize,
}

#[async_graphql::ComplexObject]
impl RecallProbability {
  pub async fn from(&self) -> String {
    let mut from = String::new();
    from.push_str(format!("{}", &self.recallpb).as_ref());
    from.push_str("<");
    from.push_str(&self.title.to_owned());
    from.push_str(">");

    from
  }
}
